/*
 * File:   main.cpp
 * Author: thiago
 *
 * Created on 8 de Novembro de 2009, 22:26
 */

#include <iostream>
#include <stdlib.h>
#include <malloc.h>
#include <cmath>
#include <math.h>
//Includes GL
#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/glext.h>
//Includes CUDA
#include <cuda_runtime.h>
#include <cutil.h>
#include <vector_types.h>
#include <cuda_gl_interop.h>
#include <cutil_gl_error.h>
#include <assert.h>
#include <cudpp/cudpp.h>
#include "Commons.h"
#include "Estruturas.h"
#include "Visualizacao/Draw.h"
#include "Inicializacao/Init.h"
#include "Visualizacao/Prints.h"
#include "Calculos/Calculos.h"

using namespace std;

#define ABS(a)		(((a) < 0) ? -(a) : (a))

void buildVBO() {
    glGenBuffers(1, &positionsVBO);
    glBindBuffer(GL_ARRAY_BUFFER, positionsVBO);
    unsigned int size = totalParticulas * sizeof (float3);
    glBufferData(GL_ARRAY_BUFFER, size, posicoesParticulas, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glGenBuffers(1, &positionsVBO2);
    glBindBuffer(GL_ARRAY_BUFFER, positionsVBO2);
    unsigned int size2 = totalParticulas * sizeof (float3);
    glBufferData(GL_ARRAY_BUFFER, size2, posicoesParticulas2, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    CUDA_SAFE_CALL(cudaGLRegisterBufferObject(positionsVBO));
    CUDA_SAFE_CALL(cudaGLRegisterBufferObject(positionsVBO2));
    CUT_CHECK_ERROR_GL();
}

void deleteVBO(){
    CUDA_SAFE_CALL(cudaGLUnregisterBufferObject(positionsVBO));
    CUDA_SAFE_CALL(cudaGLUnregisterBufferObject(positionsVBO2));
    glDeleteBuffers(1, &positionsVBO);
    glDeleteBuffers(1, &positionsVBO2);
}

void teclado(unsigned char key, int x, int y) {
    switch (key) {
        case 27:
            exit(0);
            break;
        case 'g':
        case 'G':
            mostraGrid = !mostraGrid;
            cout << "MostraGrid = " << mostraGrid << endl;
            break;
        case 'e':
        case 'E':
            mostraEixo = !mostraEixo;
            cout << "MostraEixo = " << mostraEixo << endl;
            break;
        case 'r':
        case 'R':
            mostraVetores = !mostraVetores;
            cout << "MostraVetores = " << mostraVetores << endl;
            break;
        case 'p':
        case 'P':
            mostraPressao = !mostraPressao;
            cout << "MostraPressao = " << mostraPressao << endl;
            break;
        case 'v':
        case 'V':
            mostraViscosidade = !mostraViscosidade;
            cout << "MostraViscosidade = " << mostraViscosidade << endl;
            break;
        case 'a':
        case 'A':
            mostraAceleracao = !mostraAceleracao;
            cout << "MostraAceleracao = " << mostraAceleracao << endl;
            break;
        case 'f':
        case 'F':
            temGravidade = !temGravidade;
            if (temGravidade) {
                gravidade.x = 0.0f;
                gravidade.y = -9.8f;
                gravidade.z = 0.0f;
                cout << "Gravidade ligada." << endl;
            } else {
                gravidade.x = 0.0f;
                gravidade.y = 0.0f;
                gravidade.z = 0.0f;
                cout << "Gravidade desligada." << endl;
            }
        case '1':
        //case 'S':
            if (gpu) {
                CalculaKernelsOnGPU();
            } else {
                calculaOnCPU();
            }
            break;
        case '2':
        //case 'R':
            cout << "Processo de simulação estartado!" << endl;
            if (gpu) {
                glutIdleFunc(CalculaKernelsOnGPU);
            } else {
                glutIdleFunc(calculaOnCPU);
            }
            break;
        case '3':
        //case 'P':
            cout << "Processo de simulação pausado!" << endl;
            glutIdleFunc(NULL);
            break;
    }
    glutPostRedisplay();
}

/*
 *
 */
int main(int argc, char** argv) {

    if(!verificaParametros(argc, argv)){
        exit(EXIT_SUCCESS);
    }

    //Instruções de uso.
    cout << "esc - Sai do simulador." << endl;
    cout << "g/G - Mostra o grid." << endl;
    cout << "e/E - Mostra o eixo." << endl;
    cout << "r/R - Mostra o vetor da resultante." << endl;
    cout << "p/P - Mostra o vetor da pressao." << endl;
    cout << "v/V - Mostra o vetor da viscosidade." << endl;
    cout << "a/A - Mostra o vetor de pressao." << endl;
    cout << "f/F - Liga/Desliga a gravidade." << endl;
    cout << "1 - Executa um passo de simulacao." << endl;
    cout << "2 - Inicia o processo de simulacao." << endl;
    cout << "3 - Para o processo de simuacao." << endl;

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(800, 600);
    glutInitWindowPosition(10, 10);
    glutCreateWindow("Simulador de fluido 2D");
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    }

    cudaGLSetGLDevice(0);

    init();
    
#ifdef DEBUG
    printDadosIniciais();
    printDadosParticulas();
#endif

#ifdef DEBUG
    printPosicoesParticulas();
#endif

#ifdef DEBUG_GL
    cout << "ANTES DO VBO!" << endl;
#endif
    buildVBO();
    VBO_Ativo = true;
#ifdef DEBUG_GL
    cout << "DEPOIS DO VBO!" << endl;
#endif

    //iniciaCuda();

    //OPENGL
    glutKeyboardFunc(teclado);
    glutDisplayFunc(display);
    glutReshapeFunc(AlteraTamanhoJanela);
    //glutIdleFunc(Desenha);

    glutMainLoop();

    return (EXIT_SUCCESS);
}
